TCP 协议中 Keep-Alive 特性

在腾讯面试的时候问过我基于这个特性的问题,可惜我没答出来:(,以下为原题部分。

在 TCP 连接中,我们都知道客户端要与服务器端断开连接时需要经过”四次分手”。但如果客户端在未知因素的情况下宕机了,那服务器端会在什么时候认为客户端已掉线,从而服务器端”主动”断开连接呢?

前言

抛弃上面的描述,我们知道在 TCP 协议中,如果客户端不主动断开与服务器端的连接时,服务器端便会一直持有对这个客户端的连接。如果不引入某些有效机制的话,这将会大大地消耗服务器端的资源。

keep-alive 机制确保了服务器端能够在客户端无消息发送的一段时间后,自主地断开与客户端的连接。

RFC 中 Keep-Alive 机制

keep-alive 是 TCP 协议的可选特性(optional feature)。如果操作系统实现了这一特性,就必须保证应用程序能够为每个 TCP 连接打开或关闭该特性,且这一特性必须是默认关闭的。

keep-alive 的心跳包只能够在从最后一次接收到 ACK 包的时间起,经过一个固定的时间间隔后才能发送。这个时间间隔必须能够被配置,且默认值不能够低于2小时。

keep-alive 应当在服务器端启用,而客户端不做任何修改。倘若客户端开启了这一特性,当客户端异常崩溃或者出现连接故障的话,将会导致该连接无限期挂起和消耗不必要的资源。

在 TCP 规范中并不包含 keep-alive 机制的主要原因有三:(1)在短暂的网络故障期间,可能会导致一个良好正常的连接(perfectly good connections)断开。(2)消耗不必要的带宽资源(”if no one is using the connection, who cares if it is still good?”)。(3)在以数据包计费的互联网网络中(额外)花费金钱。

Linux 内核下 Keep-Alive 的重要参数

在 Linux 内核中,keep-alive 机制涉及到三个重要的参数:

  1. tcp_keepalive_time。该参数是指最后一次数据包(不包含数据的 ACK 包)发送的时间到第一次发送的心跳包之间的时间间隔。默认值为7200s(2小时)。
  2. tcp_keepalive_intvl。该参数是指连续两个心跳包之间的时间间隔。默认值为75s。
  3. tcp_keepalive_probes。该参数是指在服务器端认为该连接失效(dead)并通知用户前,未确认的探测器(unacknowledged probes)发送的数量。默认值为9(次)。

Linux 的文档还特别声明了即使 keep-alive 这一机制在内核中被配置了,这一行为也不是 Linux 的默认行为。

面试题的一种合适的解释

了解了这一特性背后的含义时,我们可以对面试官说到。在 Linux 环境下,如果该连接中 keep-alive 机制已开启时,服务器端会在 7200s + 75s * 9time 后断开与客户端的连接(即在底层清除失效的文件描述符)。

与 HTTP 中 Keep-Alive 的对比

HTTP 协议中的 keep-alive 机制是为了通信双方的连接复用,避免消耗太多资源。而 TCP 协议中 keep-alive 机制是为了检验通信双方的是否活着(alive),保证通信能够正常进行。


参考资料:

  1. https://tools.ietf.org/html/rfc1122#page-101
  2. http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/usingkeepalive.html
  3. http://www.importnew.com/27624.html
  4. http://www.cnblogs.com/liuyong/archive/2011/07/01/2095487.html